import React from "react";
import * as THREE from "three";
import { Camera, Scene, WebGLRenderer } from "three";
import { Group3D, Line2, Mesh, MeshMaterialType, Text2D, TextAlign, THREEComponent, Transition, TransitionColor } from "xsh-react-three";
import { LinePoints } from "xsh-react-three/lib/Line/Line";
import { Vector3 } from "xsh-react-three/lib/Object3D/type";
import { THREEMouseEvent } from "xsh-react-three/lib/Renderer/THREEMouse";

export type LineArea3DData = {
    value: number;
    [key: string]: any;
}
interface LineArea3DProps {
    datas?: LineArea3DData[]
    color?: string;
    width?: number;
    height?: number;
    position?: Vector3;
    rotation?: Vector3;
    scale?: Vector3;
    // 鼠标相关
    cursor?: string;//-----------------------------鼠标样式
    onMousemove?: (e: THREEMouseEvent) => void;//--鼠标滑动事件
    onMouseleave?: (e: THREEMouseEvent) => void;//-鼠标离开事件
    onMouseenter?: (e: THREEMouseEvent) => void;//-鼠标进入事件
    onMousedown?: (e: THREEMouseEvent) => void;//--鼠标按下事件
    onMouseup?: (e: THREEMouseEvent) => void;//----鼠标弹起事件
    onClick?: (e: THREEMouseEvent) => void;//------鼠标点击事件
}

interface State { }

export default class LineArea3D extends THREEComponent<LineArea3DProps, State> {
    constructor(props: LineArea3DProps) {
        super(props);
    }

    static defaultProps = {
        datas: [1],
        color: "#0f0",
        width: 10,
        height: 10
    }

    static originPlanrGeo = new THREE.PlaneBufferGeometry(100, 100, 100, 1);

    static TempVector3 = new THREE.Vector3();

    static originMaterial = new class LineArea3DMaterial extends THREE.ShaderMaterial {

        setColor(color: string) {
            this.uniforms.color.value.set(color);
        }

        setRGB(r: number, g: number, b: number) {
            this.uniforms.color.value.setRGB(r, g, b);
        }

        uniforms = { color: { value: new THREE.Color() } }

        vertexShader = `
            varying vec2 vUv;
            void main() {
                vUv = uv;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }
        `;

        fragmentShader = `
            uniform vec3 color;
            varying vec2 vUv;

            void main() {
                gl_FragColor = vec4(color, vUv.y/2.0);
            }
        `;

        blending = THREE.NormalBlending; // 渲染时不与背景融合计算

        side = THREE.DoubleSide;
        depthTest = true;
        depthWrite = true;
        transparent = true;
        wireframe = false;
    }

    // 模型
    lineAreaGeometry = LineArea3D.originPlanrGeo.clone();

    // 材质
    lineAreaMaterial = LineArea3D.originMaterial.clone();

    TVectro3 = new Transition();

    TColor!: TransitionColor;

    componentDidMount() {
        this.setLineAreaGeometry();
        const { color = "#fff" } = this.props;
        this.TColor = new TransitionColor(color);
        this.lineAreaMaterial.setColor(color);
    }

    componentDidUpdate() {
        this.setLineAreaGeometry();
        const { color = "#fff" } = this.props;
        this.TColor.set(color);
    }

    setLineAreaGeometry = () => {
        const { datas = [] } = this.props;

        const maxValue = Math.max(...datas.map(d => d.value));

        let originPosition = LineArea3D.originPlanrGeo.attributes.position;

        let newPosition: number[] = [];

        let x = 0, y = 0, z = 0;

        for (let i = 0; i <= originPosition.count; i++) {

            x = originPosition.array[i * 3] + 50;
            y = originPosition.array[(i * 3) + 1] + 50;


            if (x < datas.length) {

                if (y > 0) {
                    y = (100 / maxValue) * datas[x].value;
                }

                x *= (100 / datas.length);

            } else {

                if (y > 0) {
                    y = 0;
                }

                x = 100;
            }

            newPosition.push(x, y, 0);
        }

        this.TVectro3
            .setBezier([0, 0, 1, 1])
            .set(newPosition);
    }

    beforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera) {

        if (!this.TVectro3.isDone()) {
            let position = this.lineAreaGeometry.attributes.position;
            let uv = this.lineAreaGeometry.attributes.uv;

            for (let i = 0; i <= position.count; i++) {

                position.setXYZ(i,
                    this.TVectro3.get(i * 3),
                    this.TVectro3.get((i * 3) + 1),
                    this.TVectro3.get((i * 3) + 2)
                );


                uv.setY(i, this.TVectro3.get((i * 3) + 1) / 100);
            }

            uv.needsUpdate = true;
            position.needsUpdate = true;
        }

        if (!this.TColor.isDone()) {
            const { r, g, b } = this.TColor.getJson();
            this.lineAreaMaterial.setRGB(r, g, b);
        }

    }

    getPoints() {
        const { datas = [], width = 10, height = 10 } = this.props;

        let points: LinePoints = [];

        let length = Math.max(1, datas.length);

        let maxValue = Math.max(...datas.map(d => d.value));

        let currenIndex = 0;

        for (let i = 0; i <= 100; i++) {

            if (datas[i]) {
                currenIndex = i;
                points.push([currenIndex * (width / length), datas[currenIndex].value * (height / maxValue), 0]);
            } else {
                points.push([(currenIndex + 1) * (width / length), 0, 0]);
            }

        }

        return points;
    }

    componentWillUnmount() {
        this.lineAreaGeometry.dispose();
        this.lineAreaMaterial.dispose();
    }

    render() {
        const {
            datas = [],
            color = "#fff",
            width = 10,
            height = 10,
            position,
            rotation,
            scale,

            // 鼠标相关
            cursor,
            onMousemove,
            onMouseleave,
            onMouseenter,
            onMousedown,
            onMouseup,
            onClick,
        } = this.props;
        const points = this.getPoints();
        return (
            <Group3D
                position={position}
                rotation={rotation}
                scale={scale}
            >

                <Line2
                    bezier={[0, 0, 1, 1]}
                    points={points}
                    color={[color]}
                    linewidth={2}
                />

                <Mesh
                    geometry={this.lineAreaGeometry}
                    material={this.lineAreaMaterial}
                    renderOrder={1}
                    scale={{ x: width / 100, y: height / 100 }}

                    cursor={cursor}
                    onMousemove={onMousemove}
                    onMouseleave={onMouseleave}
                    onMouseenter={onMouseenter}
                    onMousedown={onMousedown}
                    onMouseup={onMouseup}
                    onClick={onClick}
                />

                {
                    points.slice(0, datas.length).map((p, index) => {
                        return (
                            <Text2D
                                key={index}
                                size={1}
                                color={color}
                                text={String(p[1].toFixed(1))}
                                scale={{ x: 0.5, y: 0.5 }}
                                position={{ x: p[0], y: p[1], z: 0.1 }}
                                textAlign={TextAlign.CENTER_BOTTOM}
                            />
                        );
                    })

                }
            </Group3D>
        );
    }
}
